//
// Created by zhouwd on 2019/7/29.
//

#include "media_render.h"

#define ATTRIB_VERTEX 0
#define ATTRIB_TEXTURE 1

static const GLfloat vertexVertices[] = {
        -1.0f, -1.0f,0,
        1.0f, -1.0f,0,
        -1.0f,  1.0f,0,
        1.0f,  1.0f,0
};

static const GLfloat textureVertices[] = {
        0.0f,  1.0f,
        1.0f,  1.0f,
        0.0f,  0.0f,
        1.0f,  0.0f,
};

static bool checkGlError(const char* funcName) {
    GLint err = glGetError();
    if (err != GL_NO_ERROR) {
        printf("GL error after %s(): 0x%08x\n", funcName, err);
        return true;
    }
    return false;
}

int _MEDIA_RENDER::init(int width, int height, const char *mPreviewVertexShaderBuffer, const char *mPreviewFragmentShaderBuffer) {
    pFile = fopen ("/sdcard/zhouwd/11111.yuv", "wb");
    mWidth = width - width % 16;
    mHeight = height - height % 16;
    ALOGE("init > mWidth > %d > mHeight > %d", mWidth, mHeight);
    yBufferSize = sizeof(uint8_t) * mWidth * mHeight;
    uBufferSize = sizeof(uint8_t) * mWidth / 2 * mHeight / 2;
    vBufferSize = sizeof(uint8_t) * mWidth / 2 * mHeight / 2;

    yBuffer = (uint8_t *)malloc(yBufferSize);
    uBuffer = (uint8_t *)malloc(uBufferSize);
    vBuffer = (uint8_t *)malloc(vBufferSize);

    memset(yBuffer, 0, yBufferSize);
    memset(uBuffer, 0, uBufferSize);
    memset(vBuffer, 0, vBufferSize);

    program = createProgram(mPreviewVertexShaderBuffer, mPreviewFragmentShaderBuffer);

    if (program < 0){
        ALOGE("程序创建失败 > program > %d", program );
        return -1;
    }

    glClearColor(0,0,0,0);

    return 0;
}

int _MEDIA_RENDER::step() {

    glClear(GL_COLOR_BUFFER_BIT);
    glUseProgram(program);



//    glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT, GL_FALSE, 0, vertexVertices);
//    glEnableVertexAttribArray(ATTRIB_VERTEX);
//    glVertexAttribPointer(ATTRIB_TEXTURE, 2, GL_FLOAT, GL_FALSE, 0, textureVertices);
//    glEnableVertexAttribArray(ATTRIB_TEXTURE);

    //ALOGE("Java_com_zhouwd_z_1camera_PreviewRender_step: > %s" ,yBuffer);
    //Y
    //
    //Init Texture
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, id_y);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, mWidth, mHeight, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, yBuffer);
    glUniform1i(textureUniformY, 0);
    //U
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, id_u);
    //ALOGE("Java_com_zhouwd_z_1camera_PreviewRender_step: uBuffer > %s", previewData.uBuffer);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, mWidth/2, mHeight/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, vBuffer);
    glUniform1i(textureUniformU, 1);
    //V
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, id_v);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, mWidth/2, mHeight/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, uBuffer);
    glUniform1i(textureUniformV, 2);

    glDrawArrays(GL_TRIANGLE_STRIP,0,4);

    return 0;
}

int _MEDIA_RENDER::resize() {

    glViewport(0, 0, 1080, 720);
    glClear(GL_COLOR_BUFFER_BIT);

    return 0;
}



GLuint _MEDIA_RENDER::createShader(GLenum shaderType, const char *src) {
    ALOGE("_MEDIA_RENDER > createShader > %s" ,src);
    GLuint shader = glCreateShader(shaderType);
    ALOGE("_MEDIA_RENDER > shader > %d" ,shader);
    if (!shader) {
        checkGlError("glCreateShader");
        return 0;
    }
    glShaderSource(shader, 1, &src, NULL);

    GLint compiled = GL_FALSE;
    glCompileShader(shader);
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    ALOGE("_MEDIA_RENDER > compiled > %d" ,compiled);
    if (!compiled) {
        GLint infoLogLen = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLen);
        if (infoLogLen > 0) {
            GLchar* infoLog = (GLchar*)malloc(infoLogLen);
            if (infoLog) {
                glGetShaderInfoLog(shader, infoLogLen, NULL, infoLog);
                ALOGE("Could not compile %s shader:\n%s\n",
                       shaderType == GL_VERTEX_SHADER ? "vertex" : "fragment",
                       infoLog);
                free(infoLog);
            }
        }
        glDeleteShader(shader);
        return 0;
    }
    return shader;
}

GLuint _MEDIA_RENDER::createProgram(const char *vtxSrc, const char *fragSrc) {
    GLuint vtxShader = 0;
    GLuint fragShader = 0;
    GLuint program = 0;
    GLint linked = GL_FALSE;
    vtxShader = createShader(GL_VERTEX_SHADER, vtxSrc);
    ALOGE("createProgram > vtxShader > %d", vtxShader);
    if (!vtxShader){
        ALOGE("Could not create vertext shader");
        goto exit;
    }

    fragShader = createShader(GL_FRAGMENT_SHADER, fragSrc);
    if (!fragShader){
        ALOGE("Could not create fragment shader");
        goto exit;
    }

    program = glCreateProgram();
    if (!program) {
        checkGlError("glCreateProgram");
        goto exit;
    }
    glAttachShader(program, vtxShader);
    glAttachShader(program, fragShader);

    //glBindAttribLocation(program, ATTRIB_VERTEX, "vPosition");
    //glBindAttribLocation(program, ATTRIB_VERTEX, "a_position");
    //glBindAttribLocation(program, ATTRIB_TEXTURE, "a_texCoord");

    glLinkProgram(program);
    glGetProgramiv(program, GL_LINK_STATUS, &linked);
    vboIds = (GLuint *)(malloc(sizeof(GLuint) * 2));
    glGenBuffers(2, vboIds);
    glBindBuffer(GL_ARRAY_BUFFER, vboIds[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexVertices), vertexVertices, GL_STATIC_DRAW);
    glEnableVertexAttribArray(ATTRIB_VERTEX);
    //Set Arrays
    glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT, GL_FALSE, 0, 0);
    //Enable it

    glBindBuffer(GL_ARRAY_BUFFER, vboIds[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(textureVertices), textureVertices, GL_STATIC_DRAW);
    glEnableVertexAttribArray(ATTRIB_TEXTURE);
    glVertexAttribPointer(ATTRIB_TEXTURE, 2, GL_FLOAT, GL_FALSE, 0, 0);


    /*glGenBuffers(1, vboIds);
    //2. 绑定到顶点坐标数据缓冲
    glBindBuffer(GL_ARRAY_BUFFER, vboIds[0]);
    //3. 向顶点坐标数据缓冲送入数据
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexVertices) / sizeof(vertexVertices[0]) * 4, vertexVertices, GL_STATIC_DRAW);
    glEnableVertexAttribArray(ATTRIB_VERTEX);
    //Set Arrays
    glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT, GL_FALSE, 0, 0);
    //Enable it


    glBindBuffer(GL_ARRAY_BUFFER, 0);//现在不使用这个缓冲区

    glGenBuffers(1, vboIds);
    //2. 绑定到顶点坐标数据缓冲
    glBindBuffer(GL_ARRAY_BUFFER, vboIds[1]);
    //3. 向顶点坐标数据缓冲送入数据
    glBufferData(GL_ARRAY_BUFFER, sizeof(textureVertices) / sizeof(textureVertices[0]) * 4, textureVertices, GL_STATIC_DRAW);
    glEnableVertexAttribArray(ATTRIB_TEXTURE);
    glVertexAttribPointer(ATTRIB_TEXTURE, 2, GL_FLOAT, GL_FALSE, 0, 0);


    glBindBuffer(GL_ARRAY_BUFFER, 0);//现在不使用这个缓冲区*/


    textureUniformY = glGetUniformLocation(program, "tex_y");
    textureUniformU = glGetUniformLocation(program, "tex_u");
    textureUniformV = glGetUniformLocation(program, "tex_v");

    glGenTextures(1, &id_y);
    glBindTexture(GL_TEXTURE_2D, id_y);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glBindTexture(GL_TEXTURE_2D, 0);

    glGenTextures(1, &id_u);
    glBindTexture(GL_TEXTURE_2D, id_u);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glBindTexture(GL_TEXTURE_2D, 0);

    glGenTextures(1, &id_v);
    glBindTexture(GL_TEXTURE_2D, id_v);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glBindTexture(GL_TEXTURE_2D, 0);


    if (!linked) {
        ALOGE("Could not link program");
        GLint infoLogLen = 0;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLen);
        if (infoLogLen) {
            GLchar* infoLog = (GLchar*)malloc(infoLogLen);
            if (infoLog) {
                glGetProgramInfoLog(program, infoLogLen, NULL, infoLog);
                ALOGE("Could not link program:\n%s\n", infoLog);
                free(infoLog);
            }
        }
        glDeleteProgram(program);
        program = 0;
    }
    exit:
    glDeleteShader(vtxShader);
    glDeleteShader(fragShader);
    return program;
}

int _MEDIA_RENDER::destory() {



    return 0;
}

int _MEDIA_RENDER::updateVideoFrameData(uint8_t *data, int decodeWidth, int decodeHeight) {

    //ALOGE("_MEDIA_RENDER > updateVideoFrameData > > %s", data);

    fwrite (data , 1, decodeWidth * decodeHeight * 3 / 2, pFile);

    for (int i = 0; i < mHeight; i++) {
        memcpy(yBuffer + mWidth * i, data + decodeWidth * i, mWidth);
    }

    int vpot = 0, upot = 0;

    for (int i = 0; i < mHeight; ++i) {
        for (int j = 0; j < mWidth / 2; ++j) {
            if (j % 2 == 0) {
                memcpy(vBuffer + vpot, data + decodeWidth * decodeHeight + i * decodeWidth / 2 + j, 1);
                vpot ++;
            } else {
                memcpy(uBuffer + upot, data + decodeWidth * decodeHeight + i * decodeWidth / 2 + j, 1);
                upot ++;
            }
        }
    }

//    for (int i = 0; i < mHeight / 2; i++) {
//        memcpy(vBuffer + mWidth * i / 2, data + decodeWidth * decodeHeight + decodeWidth * i / 2, mWidth / 2);
//    }

//    for (int i = 0; i < mHeight / 2; i++) {
//        memcpy(uBuffer + mWidth * i / 2, data + decodeWidth * decodeHeight * 5 / 4 + decodeWidth * i / 2, mWidth / 2);
//    }

    if (this->mediaVideoFrameNotify != nullptr) {
        this->mediaVideoFrameNotify->updateFrame();
    }

    return 0;
}

int _MEDIA_RENDER::setMediaVideoFrameNotify(MediaVideoFrameNotify *mediaVideoFrameNotify) {
    this->mediaVideoFrameNotify = mediaVideoFrameNotify;
    return 0;
}

int _MEDIA_RENDER::prepare() {
    if (this->mediaVideoFrameNotify != nullptr) {
        this->mediaVideoFrameNotify->prepare();
    }
    return 0;
}
